package com.cf.invoker.http.client;

import com.cf.invoker.RemoteClient;
import com.cf.util.ReflectionUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cglib.proxy.Proxy;
import org.springframework.web.client.RestTemplate;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/****
 *
 * @Author zhu-mingming
 * @Description
 * @Date 2018/10/23
 *
 **/
@Slf4j
public class ClientServiceScanner implements BeanPostProcessor {

    private RestTemplate restTemplate;

    private Map<Class, Object> instanceMap = new ConcurrentHashMap<>();

    public ClientServiceScanner(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Field[] declaredFields = bean.getClass().getDeclaredFields();
        for (Field declaredField : declaredFields) {
            RemoteClient remoteClient = declaredField.getAnnotation(RemoteClient.class);
            if (remoteClient != null) {
                Object proxyInstance = createProxyInstance(declaredField.getType(), new ClientInvocationHandler(createClientMeta(remoteClient, declaredField), restTemplate));
                ReflectionUtil.setFieldValue(bean, declaredField.getName(), proxyInstance);
                log.info("#######创建【{}】代理对象,并设置bean【{}】#######成功", declaredField.getDeclaringClass(), beanName);
            }
        }
        return bean;
    }


    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }


    /**
     * 通过代理工厂的方式来创建代理类
     *
     * @param clazz 要代理的类的对象
     * @return
     */
    private Object createProxyInstance(Class clazz, ClientInvocationHandler clientInvocation) {
        Object instance = instanceMap.get(clazz);
        if (instance != null) {
            return instance;
        }
        Object proxyInstance = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, clientInvocation);
        instanceMap.put(clazz, proxyInstance);
        return proxyInstance;
    }

    private Map<Method, ClientMeta> createClientMeta(RemoteClient remoteClient, Field declaredField) {

        Class<?> declaringClass = declaredField.getType();
        Method[] methods = declaringClass.getMethods();
        if (methods == null) {
            return Collections.emptyMap();
        }
        Map<Method, ClientMeta> clientMetaMap = new HashMap<>(methods.length);
        for (Method mt : methods) {
            ClientMeta clientMeta = new ClientMeta();
            clientMeta
                    .setHasArguments(mt.getParameterCount() > 0)
                    .setHasReturn(mt.getReturnType() != null)
                    .setRemoteClient(remoteClient)
                    .setMethodParentClass(declaredField.getDeclaringClass());
            
            //设置是否有返回类型
            if (clientMeta.isHasReturn()) {
                clientMeta.setResponseType(mt.getReturnType());
            }

            //设置时候有返回参数
            if (clientMeta.isHasArguments()) {
                clientMeta.setArgumentTypes(Arrays.asList(mt.getParameterTypes()));
            }
            clientMeta.setMethod(mt);
            clientMetaMap.put(mt, clientMeta);
        }
        return clientMetaMap;
    }


}
